furious 网络扫描器 代码学习
furious 网络扫描器 代码学习
最近在学习go,用go写的代码都会康康
地址:https://github.com/liamg/furious
Furious is a fast, lightweight, portable network scanner.
看介绍,扫描6000个端口只发送一个sync包,用时4秒,安装要求上需要libpcap
,之前ksubdomain也是用的libpcap
,所以康康它咋写的
ctx, cancel := context.WithCancel(context.Background())
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
<-c
fmt.Println("Scan cancelled. Requesting stop...")
cancel()
}()
在主线程中定义信号量,方便取消。
扫描的主函数
整个扫描流程中会先依次调用Start()
、Scan()
、然后使用range results
取得结果
创建扫描器
可以看到扫描类型有五种stealth
、syn
、fast
、connect
、device
为扫描器实现了一个接口
三种扫描类型都实现了这个接口
scan-connect
先看看scan-connect.go
start
中实现了一个小协程池,并使用tcp连接 扫描端口
scan
中先遍历ip再遍历端口组合数据jobChan
数据
全部扫描结束后才会返回数据。
scan-device
start()
为空,主要逻辑在scan()
函数
调用第三方arp库github.com/mostlygeek/arp
扫描
后面也会对每个ip的1端口建立一个连接来确定延迟
scan-syn
它的扫描逻辑主要调用pcap
,然后自己组合tcp包发送
贴一下代码叭,一看就能懂大概,源代码在https://github.com/liamg/furious/blob/master/scan/scan-syn.go
func (s *SynScanner) scanHost(job hostJob) (Result, error) {
result := NewResult(job.ip)
select {
case <-job.ctx.Done():
return result, nil
default:
}
router, err := routing.New()
if err != nil {
return result, err
}
networkInterface, gateway, srcIP, err := router.Route(job.ip)
if err != nil {
return result, err
}
handle, err := pcap.OpenLive(networkInterface.Name, 65535, true, pcap.BlockForever)
if err != nil {
return result, err
}
defer handle.Close()
openChan := make(chan int)
closedChan := make(chan int)
filteredChan := make(chan int)
doneChan := make(chan struct{})
startTime := time.Now()
go func() {
for {
select {
case open := <-openChan:
if open == 0 {
close(doneChan)
return
}
if result.Latency < 0 {
result.Latency = time.Since(startTime)
}
for _, existing := range result.Open {
if existing == open {
continue
}
}
result.Open = append(result.Open, open)
case closed := <-closedChan:
if result.Latency < 0 {
result.Latency = time.Since(startTime)
}
for _, existing := range result.Closed {
if existing == closed {
continue
}
}
result.Closed = append(result.Closed, closed)
case filtered := <-filteredChan:
if result.Latency < 0 {
result.Latency = time.Since(startTime)
}
for _, existing := range result.Filtered {
if existing == filtered {
continue
}
}
result.Filtered = append(result.Filtered, filtered)
}
}
}()
rawPort, err := freeport.GetFreePort()
if err != nil {
return result, err
}
// 首先获取网关的mac地址
hwaddr, err := s.getHwAddr(job.ip, gateway, srcIP, networkInterface)
if err != nil {
return result, err
}
// 组合网络的各个数据层
eth := layers.Ethernet{
SrcMAC: networkInterface.HardwareAddr,
DstMAC: hwaddr,
EthernetType: layers.EthernetTypeIPv4,
}
ip4 := layers.IPv4{
SrcIP: srcIP,
DstIP: job.ip,
Version: 4,
TTL: 255,
Protocol: layers.IPProtocolTCP,
}
tcp := layers.TCP{
SrcPort: layers.TCPPort(rawPort),
DstPort: 0,
SYN: true,
}
tcp.SetNetworkLayerForChecksum(&ip4)
listenChan := make(chan struct{})
ipFlow := gopacket.NewFlow(layers.EndpointIPv4, job.ip, srcIP)
// 接收数据
go func() {
eth := &layers.Ethernet{}
ip4 := &layers.IPv4{}
tcp := &layers.TCP{}
parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, eth, ip4, tcp)
for {
select {
case <-job.ctx.Done():
break
default:
}
// Read in the next packet.
data, _, err := handle.ReadPacketData()
if err == pcap.NextErrorTimeoutExpired {
break
} else if err == io.EOF {
break
} else if err != nil {
// connection closed
fmt.Printf("Packet read error: %s\n", err)
continue
}
decoded := []gopacket.LayerType{}
if err := parser.DecodeLayers(data, &decoded); err != nil {
continue
}
for _, layerType := range decoded {
switch layerType {
case layers.LayerTypeIPv4:
if ip4.NetworkFlow() != ipFlow {
continue
}
case layers.LayerTypeTCP:
if tcp.DstPort != layers.TCPPort(rawPort) {
continue
} else if tcp.SYN && tcp.ACK {
openChan <- int(tcp.SrcPort)
} else if tcp.RST {
closedChan <- int(tcp.SrcPort)
}
}
}
}
close(listenChan)
}()
for _, port := range job.ports {
tcp.DstPort = layers.TCPPort(port)
_ = s.send(handle, ð, &ip4, &tcp)
}
timer := time.AfterFunc(s.timeout, func() { handle.Close() })
defer timer.Stop()
<-listenChan
close(openChan)
<-doneChan
return result, nil
}
获取一个本地没使用的端口作为发送端口,截取返回的数据包,如果包含了这个端口说明端口开放。
下载公共端口数据
https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.csv
下载文件处理后,会保存为known.go
其他
并发方面有点混乱,pcap获取网关地址那部分值得学习,其他地方,感觉一般般,用来扫内网还行,扫外网速度也不行。